/**
 * Copyright &copy; 2016-2017 <a href="http://git.oschina.net/whoamien/backend_management">Backend Management</a> All rights reserved.
 */
package com.xw.util;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;

/**
 * The Class StringUtil.
 *
 * @author Xi Wei
 * @version 1.0
 * @since 1.0
 */
public class StringUtil {

	/** The Constant SPL_CHARSET. */
	public final static String[] SPL_CHARSET = new String[] { "~", "`", "!", "@", "#", "$", "%", "^", "&", "*", "-",
			"_", "=", "+", "{", "}", "[", "]", "|", "'", "\"", ":", ";", "?", ">", "<", ".", ",", " ", "(", ")", "\\",
			"/" };

	/**
	 * Dup.
	 *
	 * @param str the str
	 * @param n the n
	 * @return the string
	 */
	public static final String dup(String str, int n) {
		StringBuffer result = new StringBuffer();

		for (int i = 0; i < n; i++) {
			result.append(str);
		}

		return (result.toString());
	}

	/**
	 * L pad.
	 *
	 * @param str the str
	 * @param length the length
	 * @param padStr the pad str
	 * @return the string
	 */
	public static final String lPad(String str, int length, String padStr) {
		int lOriginal = str.length();
		int lPadStr = padStr.length();
		int times2Pad = 0;
		int lPadded = 0;

		if (lOriginal >= length) {
			return str;
		}

		StringBuffer sb = new StringBuffer();
		String padded;
		times2Pad = (length - lOriginal) / lPadStr;
		padded = dup(padStr, times2Pad);
		lPadded = padded.length();
		sb.append(padded);
		if ((lOriginal + lPadded) < length) {
			int more = length - (lOriginal + lPadded);
			sb.append(padStr.substring(0, more));
		}
		sb.append(str);
		return sb.toString();
	}

	/**
	 * R pad.
	 *
	 * @param str the str
	 * @param length the length
	 * @param padStr the pad str
	 * @return the string
	 */
	public static final String rPad(String str, int length, String padStr) {
		int lOriginal = str.length();
		int lPadStr = padStr.length();
		int times2Pad = 0;
		int lPadded = 0;

		if (lOriginal >= length) {
			return str;
		}

		StringBuffer sb = new StringBuffer(str);
		String padded;
		times2Pad = (length - lOriginal) / lPadStr;
		padded = dup(padStr, times2Pad);
		lPadded = padded.length();
		sb.append(padded);

		if ((lOriginal + lPadded) < length) {
			int more = length - (lOriginal + lPadded);
			sb.append(padStr.substring(0, more));
		}

		return sb.toString();
	}

	/**
	 * Checks if is byte array zero.
	 *
	 * @param byteArray the byte array
	 * @return true, if is byte array zero
	 */
	public static boolean isByteArrayZero(byte[] byteArray) {
		for (int i = 0; i < byteArray.length; i++) {
			if (byteArray[i] != 0) {
				return false;
			}
		}

		return true;
	}

	/**
	 * Checks if is char string.
	 *
	 * @param s the s
	 * @return true, if is char string
	 */
	public static boolean isCharString(String s) {
		char a;

		try {
			for (int i = 0; i < s.trim().length(); i++) {
				a = s.charAt(i);

				if ((!Character.isWhitespace(a)) && (!Character.isLetter(a))) {
					return false;
				}
			}

			return true;
		} catch (NullPointerException npe) {
			return true;
		}
	}

	/**
	 * Checks if is correct length.
	 *
	 * @param str the str
	 * @param intSize the int size
	 * @return true, if is correct length
	 */
	public static boolean isCorrectLength(String str, int intSize) {
		if (str == null) {
			return false;
		}

		int intLen = str.length();

		return (intLen == intSize) ? true : false;
	}

	/**
	 * Checks if is less max length.
	 *
	 * @param str the str
	 * @param intSize the int size
	 * @return true, if is less max length
	 */
	public static boolean isLessMaxLength(String str, int intSize) {
		if (str == null) {
			return false;
		}

		int intLen = str.length();

		return (intLen <= intSize) ? true : false;
	}

	/**
	 * Checks if is numeric string.
	 *
	 * @param s the s
	 * @return true, if is numeric string
	 */
	public static boolean isNumericString(String s) {
		char a;

		try {
			for (int i = 0; i < s.trim().length(); i++) {
				a = s.charAt(i);

				if ((!Character.isDigit(a))) {
					return false;
				}
			}

			return true;
		} catch (NullPointerException npe) {
			return true;
		}
	}

	/**
	 * Checks if is string.
	 *
	 * @param s the s
	 * @return true, if is string
	 */
	public static boolean isString(String s) {
		char a;

		try {
			for (int i = 0; i < s.trim().length(); i++) {
				a = s.charAt(i);

				if ((!Character.isWhitespace(a)) && (!Character.isLetterOrDigit(a)) && (a != '#') && (a != '-')) {
					return false;
				}
			}

			return true;
		} catch (NullPointerException npe) {
			return true;
		}
	}

	/**
	 * Checks if is valid double.
	 *
	 * @param str the str
	 * @param booNeg the boo neg
	 * @return true, if is valid double
	 */
	public static boolean isValidDouble(String str, boolean booNeg) {
		double dblString;

		if (str == null) {
			return false;
		}

		try {

			dblString = Double.parseDouble(str.trim());
		} catch (Exception exc) {
			return false;
		}

		if ((!booNeg) && (dblString < 0)) {
			return false;
		}

		return true;
	}

	/**
	 * Checks if is white space.
	 *
	 * @param start the start
	 * @param limit the limit
	 * @param str the str
	 * @return true, if is white space
	 */
	public static boolean isWhiteSpace(int start, int limit, String str) {
		String strSpace = str.substring(start, limit);

		char a;

		try {
			for (int i = 0; i < strSpace.length(); i++) {
				a = strSpace.charAt(i);

				if (!Character.isWhitespace(a)) {
					return false;
				}
			}

			return true;
		} catch (NullPointerException npe) {
			return true;
		}
	}

	/**
	 * Convert int to str of length.
	 *
	 * @param value the value
	 * @param length the length
	 * @return the string
	 */
	public static String convertIntToStrOfLength(int value, int length) {
		String intStr = Integer.valueOf(value).toString();
		int lenDiff = length - intStr.length();

		for (int i = 0; i < lenDiff; i++) {
			intStr = "0" + intStr;
		}

		return intStr;
	}

	/**
	 * Convert long to str of length.
	 *
	 * @param value the value
	 * @param length the length
	 * @return the string
	 */
	public static String convertLongToStrOfLength(long value, int length) {
		String longStr = Long.valueOf(value).toString();
		int lenDiff = length - longStr.length();

		for (int i = 0; i < lenDiff; i++) {
			longStr = "0" + longStr;
		}

		return longStr;
	}

	/**
	 * De null.
	 *
	 * @param obj the obj
	 * @return the string
	 */
	public static String deNull(Object obj) {
		if (obj == null) {
			return "";
		}

		return obj.toString();
	}

	/**
	 * De null.
	 *
	 * @param obj the obj
	 * @param substitute the substitute
	 * @return the string
	 */
	public static String deNull(Object obj, String substitute) {
		if (obj == null) {
			return substitute;
		}

		return obj.toString();
	}

	/**
	 * Left align str.
	 *
	 * @param value the value
	 * @param length the length
	 * @param character the character
	 * @return the string
	 */
	public static String leftAlignStr(String value, int length, char character) {
		while (value.length() < length) {
			value += character;
		}

		return value;
	}

	/**
	 * Removes the special characters.
	 *
	 * @param str the str
	 * @return the string
	 */
	public static String removeSpecialCharacters(String str) {
		if (str == null || str.trim().equals("")) {
			return "";
		}

		String tempVal1 = "";
		String tempVal2 = "";

		for (int i = 0; i < SPL_CHARSET.length; i++) {
			tempVal2 = tempVal1 + " ";
			tempVal1 = tempVal2;
		}

		str = str + tempVal1;

		for (int i = 0; i < SPL_CHARSET.length; i++) {
			str = replaceStr(str, SPL_CHARSET[i], "");
		}

		return str;
	}

	/**
	 * Replace str.
	 *
	 * @param msgToReplace the msg to replace
	 * @param searchKey the search key
	 * @param value the value
	 * @return the string
	 */
	public static String replaceStr(String msgToReplace, String searchKey, String value) {
		return replaceStr(msgToReplace, searchKey, value, "", "");
	}

	/**
	 * Replace str.
	 *
	 * @param msgToReplace the msg to replace
	 * @param searchKey the search key
	 * @param value the value
	 * @param msgPrefix the msg prefix
	 * @param msgSuffix the msg suffix
	 * @return the string
	 */
	public static String replaceStr(String msgToReplace, String searchKey, String value, String msgPrefix,
			String msgSuffix) {
		if (msgPrefix == null) {
			msgPrefix = "";
		}
		if (msgSuffix == null) {
			msgSuffix = "";
		}
		if (searchKey == null || msgToReplace == null || value == null) {
			return msgToReplace;
		}

		int count = 0;
		String msg = new String(msgToReplace);
		String key = msgPrefix + searchKey + msgSuffix;
		int pos = -1;

		while ((pos = msg.indexOf(key, count)) >= 0) {

			StringBuffer sb = new StringBuffer(msg);
			sb.replace(pos, pos + key.length(), value);
			count = pos + value.length();

			msg = sb.toString();
		}

		return msg;
	}

	/**
	 * Right align str.
	 *
	 * @param value the value
	 * @param length the length
	 * @param character the character
	 * @return the string
	 */
	public static String rightAlignStr(String value, int length, char character) {
		while (value.length() < length) {
			value = character + value;
		}

		return value;
	}

	/**
	 * Tokenize.
	 *
	 * @param tokenizeStr the tokenize str
	 * @param delimiter the delimiter
	 * @return the collection
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public static Collection tokenize(String tokenizeStr, String delimiter) {
		StringTokenizer st = new StringTokenizer(tokenizeStr, delimiter);
		ArrayList originalTypes = new ArrayList();

		while (st.hasMoreTokens()) {
			String type = st.nextToken();
			originalTypes.add(type);
		}

		return originalTypes;
	}

	/**
	 * Tokenize.
	 *
	 * @param tokenizeStr the tokenize str
	 * @return the collection
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public static Collection tokenize(String tokenizeStr) {

		ArrayList originalTypes = new ArrayList();
		for (int i = 0; i < tokenizeStr.length(); i++) {

			String nameResult = null;
			if (i == 0) {
				nameResult = tokenizeStr.substring(0, 1);
			} else {
				nameResult = tokenizeStr.substring(i, i + 1);
			}
			originalTypes.add(nameResult);
		}

		return originalTypes;

	}

	/**
	 * Trim.
	 *
	 * @param str the str
	 * @return the string
	 */
	public static String trim(String str) {
		if ((str == null) || (str.length() == 0)) {
			return str;
		}

		return str.trim();
	}

	/**
	 * To upper case.
	 *
	 * @param str the str
	 * @return the string
	 */
	public static String toUpperCase(String str) {
		if ((str == null) || (str.length() == 0)) {
			return str;
		}

		return str.toUpperCase();
	}

	/**
	 * Checks if is null or empty.
	 *
	 * @param str the str
	 * @return true, if is null or empty
	 */
	public static boolean isNullOrEmpty(String str) {
		if ((str == null) || (str.trim().length() == 0)) {
			return true;
		}
		return false;
	}

	/**
	 * Mask string except last few char.
	 *
	 * @param toBeMasked the to be masked
	 * @param maskedChar the masked char
	 * @param lenOfUnmaskStr the len of unmask str
	 * @param lenOfUnmaskEnd the len of unmask end
	 * @return the string
	 */
	public static String maskStringExceptLastFewChar(String toBeMasked, String maskedChar, int lenOfUnmaskStr,
			int lenOfUnmaskEnd) {
		String maskedStr = StringUtil.trim(toBeMasked);
		if (!StringUtil.isNullOrEmpty(toBeMasked)) {
			int lenOfStr = toBeMasked.length();
			int lenOfStrToBeMasked = lenOfStr - lenOfUnmaskEnd - lenOfUnmaskStr;
			if (lenOfStrToBeMasked > 0) {
				String tmpMaskedStr = "";
				maskedStr = StringUtil.lPad(tmpMaskedStr, lenOfStrToBeMasked, maskedChar);
				String unmaskedStrStart = toBeMasked.substring(0, lenOfUnmaskStr);
				String unmaskedStrEnd = toBeMasked.substring(lenOfStrToBeMasked + lenOfUnmaskStr);
				maskedStr = unmaskedStrStart + maskedStr + unmaskedStrEnd;
			} else {
				return maskedStr;
			}
		}
		return maskedStr;
	}

	/**
	 * Replace HTML sensitive.
	 *
	 * @param value the value
	 * @return the string
	 */
	public static String replaceHTMLSensitive(String value) {

		if (value == null)
			return (null);

		char content[] = new char[value.length()];
		value.getChars(0, value.length(), content, 0);
		StringBuffer result = new StringBuffer(content.length + 50);
		for (int i = 0; i < content.length; i++) {
			switch (content[i]) {
			case '<':
				result.append("&lt;");
				break;
			case '>':
				result.append("&gt;");
				break;
			case '&':
				result.append("&amp;");
				break;
			case '"':
				result.append("&quot;");
				break;
			case '\'':
				result.append("&#39;");
				break;
			default:
				result.append(content[i]);
			}
		}
		return (result.toString());

	}

	/**
	 * Checks if is numeric string for ID.
	 *
	 * @param s the s
	 * @return true, if is numeric string for ID
	 */
	public static boolean isNumericStringForID(String s) {
		char a;

		try {
			for (int i = 0; i < s.length(); i++) {
				a = s.charAt(i);

				if ((!Character.isDigit(a))) {
					return false;
				}
			}

			return true;
		} catch (NullPointerException npe) {
			return true;
		}
	}

	/**
	 * Parses the map.
	 *
	 * @param s the s
	 * @param separator the separator
	 * @return the map
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public static Map parseMap(String s, String separator) {
		if (s == null || separator == null) {
			return null;
		}
		Map map = new HashMap();
		if (s.length() == 0) {
			return map;
		}
		if (s.charAt(0) == '{') {
			s = s.substring(1);
		}
		if (s.charAt(s.length() - 1) == '}') {
			s = s.substring(0, s.length() - 1);
		}
		Collection c = StringUtil.tokenize(s, separator);
		List param = new ArrayList();
		param.addAll(c);
		for (int i = 0; i < param.size(); i++) {
			String ss = (String) param.get(i);
			c = StringUtil.tokenize(ss, "=");
			List l = new ArrayList();
			l.addAll(c);
			if (l != null && !l.isEmpty()) {
				Object key = l.get(0);
				Object value = null;
				if (l.size() == 2) {
					value = l.get(1);
				}
				if (key != null) {
					if (value != null && !value.equals("")) {
						map.put(key, value);
					} else {
						map.put(key, null);
					}
				}
			}
		}
		return map;
	}

	/**
	 *  Formats a map.
	 *
	 * @param map the map
	 * @return the string
	 */
	@SuppressWarnings("rawtypes")
	public static String formatMap(Map map) {
		if (map != null) {
			return map.toString();
		}
		return null;
	}

	/**
	 * Format map.
	 *
	 * @param map the map
	 * @param separator the separator
	 * @param enclosing the enclosing
	 * @return the string
	 */
	@SuppressWarnings("rawtypes")
	public static String formatMap(Map map, String separator, boolean enclosing) {
		StringBuffer sb = new StringBuffer();
		if (map == null || separator == null) {
			return null;
		}
		Iterator iter = map.keySet().iterator();
		if (enclosing) {
			sb.append("{");
		}
		while (iter.hasNext()) {
			Object key = iter.next();
			Object value = map.get(key);
			if (key != null) {
				sb.append(key).append("=");
				if (value != null) {
					sb.append(value);
				}
				sb.append(separator);
			}
		}
		int len = separator.length();
		if (sb.length() > len) {
			int startIndex = sb.length() - len;
			int endIndex = sb.length();
			String s = sb.substring(startIndex, endIndex);
			if (s.equals(separator)) {
				for (int i = 0; i < len; i++) {
					sb.deleteCharAt(sb.length() - 1);
				}
			}
		}
		if (enclosing) {
			sb.append("}");
		}
		return sb.toString();
	}

	/**
	 * Extract.
	 *
	 * @param str the str
	 * @return the integer
	 */
	public static Integer extract(String str) {
		if (str == null) {
			return null;
		}

		StringTokenizer token = new StringTokenizer(str.trim(), " ");

		while (token.hasMoreTokens()) {
			String tmp = token.nextToken();
			if (StringUtil.isNumericString(tmp)) {
				return Integer.valueOf(tmp);
			}
		}

		return null;
	}

	/**
	 * Html write.
	 *
	 * @param s the s
	 * @return the string
	 */
	public static String htmlWrite(String s) {
		String ret = "";

		if (s == null || "".equals(s)) {
			return ret;
		}

		ret = s.replaceAll("<", "&lt;");
		ret = ret.replaceAll(">", "&gt;");

		return ret;
	}

	/**
	 * Html write special handle block msg.
	 *
	 * @param s the s
	 * @return the string
	 */
	public static String htmlWriteSpecialHandleBlockMsg(String s) {
		String s1 = "";
		if (s == null || "".equals(s.trim())) {
			return s1;
		}

		int beginIndex = 0;
		for (int i = 0; i <= s.length(); i++) {
			if (i > 7) { // check for <script>
				if ("<".equalsIgnoreCase(s.substring(i - 8, i - 7)) && "s".equalsIgnoreCase(s.substring(i - 7, i - 6))
						&& "c".equalsIgnoreCase(s.substring(i - 6, i - 5))
						&& "r".equalsIgnoreCase(s.substring(i - 5, i - 4))
						&& "i".equalsIgnoreCase(s.substring(i - 4, i - 3))
						&& "p".equalsIgnoreCase(s.substring(i - 3, i - 2))
						&& "t".equalsIgnoreCase(s.substring(i - 2, i - 1))
						&& ">".equalsIgnoreCase(s.substring(i - 1, i))) {
					s1 = s1 + s.substring(beginIndex, i - 8) + "&lt;" + s.substring(i - 7, i - 1) + "&gt;";
					beginIndex = i;
				}
			}
		}
		if (beginIndex < s.length()) {
			s1 = s1 + s.substring(beginIndex, s.length());
		}

		s = s1;
		s1 = "";
		beginIndex = 0;
		for (int i = 0; i <= s.length(); i++) {
			if (i > 8) { // check for <script>
				if ("<".equalsIgnoreCase(s.substring(i - 9, i - 8)) && "/".equalsIgnoreCase(s.substring(i - 8, i - 7))
						&& "s".equalsIgnoreCase(s.substring(i - 7, i - 6))
						&& "c".equalsIgnoreCase(s.substring(i - 6, i - 5))
						&& "r".equalsIgnoreCase(s.substring(i - 5, i - 4))
						&& "i".equalsIgnoreCase(s.substring(i - 4, i - 3))
						&& "p".equalsIgnoreCase(s.substring(i - 3, i - 2))
						&& "t".equalsIgnoreCase(s.substring(i - 2, i - 1))
						&& ">".equalsIgnoreCase(s.substring(i - 1, i))) {
					s1 = s1 + s.substring(beginIndex, i - 9) + "&lt;" + s.substring(i - 8, i - 1) + "&gt;";
					beginIndex = i;
				}
			}
		}
		if (beginIndex < s.length()) {
			s1 = s1 + s.substring(beginIndex, s.length());
		}

		return s1;

	}

	/**
	 * Hex to binary str.
	 *
	 * @param hexValue the hex value
	 * @return the string
	 */
	public static String hexToBinaryStr(String hexValue) {

		if (StringUtils.isBlank(hexValue)) {
			return "0";
		}

		int maxLen = 10;
		int len = hexValue.length();
		int times = len / maxLen;
		if ((len % maxLen) > 0) {
			times++;
		}

		StringBuffer bineryValueSb = new StringBuffer();

		for (int i = 0; i < times; i++) {

			int start = i * maxLen;
			int end = (i + 1) * maxLen;
			String tmpVal = StringUtils.substring(hexValue, start, end);
			long parsedLongValue = Long.parseLong(tmpVal, 16);

			String binaryValue = Long.toBinaryString(parsedLongValue);

			if (i != 0) {
				binaryValue = StringUtils.leftPad(binaryValue, tmpVal.length() * 4, "0");
			}

			bineryValueSb.append(binaryValue);

		}

		return bineryValueSb.toString();

	}

	/**
	 * Checks if is null obj or empty string.
	 *
	 * @param obj the obj
	 * @return true, if is null obj or empty string
	 */
	public static boolean isNullObjOrEmptyString(Object obj) {

		if (null == obj)
			return true;

		if (obj instanceof String) {
			return isNullOrEmpty((String) obj);
		}

		return false;

	}

	/**
	 * Gen random numbers.
	 *
	 * @param len the len
	 * @return the string
	 */
	public static String genRandomNumbers(int len) {

		if (len < 4)
			len = 4;

		StringBuilder str = new StringBuilder("");
		str.append((int) (Math.random() * 9 + 1));

		for (int i = 0; i < len - 1; i++) {
			str.append((int) (Math.random() * 10));
		}

		return str.toString();
	}

	/**
	 * Map to json str.
	 *
	 * @param m the m
	 * @return the string
	 */
	public static String mapToJsonStr(Map<String, Object> m) {
		if (m == null || m.isEmpty())
			return "";

		StringBuilder sb = new StringBuilder("{");
		Iterator<String> it = m.keySet().iterator();
		while (it.hasNext()) {
			String k = it.next();
			Object v = (String) m.get(k);

			sb.append("\"").append(k).append("\"").append(":");
			sb.append("\"").append(v).append("\"").append(",");
		}

		String str = sb.toString().substring(0, sb.length() - 1);

		return str + "}";

	}

	/**
	 * Compare string.
	 *
	 * @param str1 the str 1
	 * @param str2 the str 2
	 * @return true, if successful
	 */
	public static boolean compareString(String str1, String str2) {
		if (str1 == null && str2 == null) {
			return true;
		}
		if (str1 == null || str2 == null) {
			return false;
		}
		if (str1.trim().equals(str2.trim())) {
			return true;
		}
		return false;
	}

	/**
	 * Format.
	 *
	 * @param str the str
	 * @param args the args
	 * @return the string
	 */
	public static String format(String str, Object... args) {

		// 这里用于验证数据有效性
		if (str == null || "".equals(str))
			return "";
		if (args.length == 0) {
			return str;
		}

		/*
		 * 如果用于生成SQL语句，这里用于在字符串前后加单引号 for(int i=0;i<args.length;i++) { String
		 * type="java.lang.String";
		 * if(type.equals(args[i].getClass().getName()))
		 * args[i]="'"+args[i]+"'"; }
		 */

		String result = str;

		// 这里的作用是只匹配{}里面是数字的子字符串
		java.util.regex.Pattern p = java.util.regex.Pattern.compile("\\{(\\d+)\\}");
		java.util.regex.Matcher m = p.matcher(str);

		while (m.find()) {
			// 获取{}里面的数字作为匹配组的下标取值
			int index = Integer.parseInt(m.group(1));

			// 这里得考虑数组越界问题，{1000}也能取到值么？？
			if (index < args.length) {

				// 替换，以{}数字为下标，在参数数组中取值
				result = result.replace(m.group(), args[index].toString());
			}
		}
		return result;
	}

	/**
	 * 本方法封装了往前台设置的header,contentType等信息.
	 *
	 * @param message            需要传给前台的数据
	 * @param type            指定传给前台的数据格式,如"html","json"等
	 * @param response            HttpServletResponse对象
	 * @throws IOException Signals that an I/O exception has occurred.
	 * @createDate 2010-12-31 17:55:41
	 */
	public static void writeToWeb(String message, String type, HttpServletResponse response) throws IOException {
		response.setHeader("Pragma", "No-cache");
		response.setHeader("Cache-Control", "no-cache");
		response.setContentType("text/" + type + "; charset=utf-8");
		response.getWriter().write(message);
		response.getWriter().close();
	}

	/**
	 * 驼峰命名法工具.
	 *
	 * @param s the s
	 * @return toCamelCase("hello_world") == "helloWorld"
	 *         toCapitalizeCamelCase("hello_world") == "HelloWorld"
	 */
	public static String toCamelCase(String s) {
		if (s == null) {
			return null;
		}

		s = s.toLowerCase();

		StringBuilder sb = new StringBuilder(s.length());
		boolean upperCase = false;
		for (int i = 0; i < s.length(); i++) {
			char c = s.charAt(i);

			if (c == '_') {
				upperCase = true;
			} else if (upperCase) {
				sb.append(Character.toUpperCase(c));
				upperCase = false;
			} else {
				sb.append(c);
			}
		}

		return sb.toString();
	}

	/**
	 * 驼峰命名法工具.
	 *
	 * @param s the s
	 * @return toCamelCaseWithSpace("hello_world") == "Hello World"
	 */
	public static String toCamelCaseWithSpace(String s) {
		if (s == null) {
			return null;
		}

		s = s.toLowerCase();

		StringBuilder sb = new StringBuilder(s.length());
		boolean upperCase = false;
		for (int i = 0; i < s.length(); i++) {
			char c = s.charAt(i);

			if (c == '_') {
				upperCase = true;
			} else if (upperCase) {
				sb.append(" " + Character.toUpperCase(c));
				upperCase = false;
			} else {
				sb.append(c);
			}
		}

		return sb.toString();
	}

	/**
	 * 驼峰命名法工具.
	 *
	 * @param s the s
	 * @return toCamelCase("hello_world") == "helloWorld"
	 *         toCapitalizeCamelCase("hello_world") == "HelloWorld"
	 */
	public static String toCapitalizeCamelCase(String s) {
		if (s == null) {
			return null;
		}
		s = toCamelCase(s);
		return s.substring(0, 1).toUpperCase() + s.substring(1);
	}

	/**
	 * Substring after last.
	 *
	 * @param str the str
	 * @param separator the separator
	 * @return the string
	 */
	public static String substringAfterLast(String str, String separator) {
		if (str == null) {
			return null;
		}
		int index = str.lastIndexOf(separator);
		if (index < 0) {
			return str;
		}
		return str.substring(index + 1);
	}

}